﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Phoenix.Xna.Framework.Graphics.Primitives
{
    public class Dome
    {
        private Engine _engine;
        private float _scale;
        private int _rings;
        private int _segments;
        private int _domeVertexSize;
        private int _domeIndexSize;
        private VertexDeclaration _vertexDeclaration;
        private VertexPositionTexture[] _domeVertexBuffer;
        private short[] _domeIndexBuffer;

        public Dome(Engine engine, int radius, float scale)
        {
            _engine = engine;
            _rings = radius / 2;
            _segments = radius;
            _scale = scale;
            GenerateVertexBuffer();
            GenerateIndexBuffer();
            _vertexDeclaration = new VertexDeclaration(engine.GraphicsDevice, VertexPositionTexture.VertexElements);
        }

        public void Draw()
        {
            _engine.GraphicsDevice.VertexDeclaration = _vertexDeclaration;
            _engine.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionTexture>(
                PrimitiveType.TriangleList,
                _domeVertexBuffer,
                0,
                _domeVertexSize,
                _domeIndexBuffer,
                0,
                _domeIndexSize);
        }

        private void GenerateVertexBuffer()
        {
            int index = 0;
            _domeVertexSize = (_segments * _rings) * 2;
            _domeVertexBuffer = new VertexPositionTexture[_domeVertexSize];

            for (int i = 0; i < _segments; i++)
            {
                double phi = 100.0f * (i / ((float)_segments - 1)) * MathHelper.Pi / 180.0;

                for (int j = 0; j < _rings; j++)
                {
                    double theta = MathHelper.Pi * j / (_rings - 1);
                    _domeVertexBuffer[index] = new VertexPositionTexture();
                    _domeVertexBuffer[index].Position.X = (float)(Math.Sin(phi) * Math.Cos(theta));
                    _domeVertexBuffer[index].Position.Y = (float)Math.Cos(phi);
                    _domeVertexBuffer[index].Position.Z = (float)(Math.Sin(phi) * Math.Sin(theta));
                    _domeVertexBuffer[index].Position *= _scale;
                    _domeVertexBuffer[index].TextureCoordinate.X = (0.5f / (float)_segments) + (i / (float)_segments);
                    _domeVertexBuffer[index].TextureCoordinate.Y = (0.5f / (float)_rings) + (j / (float)_rings);
                    index++;
                }
            }

            for (int i = 0; i < _segments; i++)
            {
                double phi = 100.0 * (i / (float)(_segments - 1)) * MathHelper.Pi / 180.0;

                for (int j = 0; j < _rings; j++)
                {
                    double theta = (MathHelper.Pi * 2.0) - (MathHelper.Pi * j / (_rings - 1));
                    _domeVertexBuffer[index] = new VertexPositionTexture();
                    _domeVertexBuffer[index].Position.X = (float)(Math.Sin(phi) * Math.Cos(theta));
                    _domeVertexBuffer[index].Position.Y = (float)Math.Cos(phi);
                    _domeVertexBuffer[index].Position.Z = (float)(Math.Sin(phi) * Math.Sin(theta));
                    _domeVertexBuffer[index].Position *= _scale;
                    _domeVertexBuffer[index].TextureCoordinate.X = (0.5f / (float)_segments) + (i / (float)_segments);
                    _domeVertexBuffer[index].TextureCoordinate.Y = (0.5f / (float)_rings) + (j / (float)_rings);
                    index++;
                }
            }
        }

        private void GenerateIndexBuffer()
        {
            int index = 0;
            _domeIndexSize = ((_segments - 1) * (_rings - 1) * 2) * 2;
            _domeIndexBuffer = new short[_domeIndexSize * 3];

            for (short i = 0; i < _segments - 1; i++)
            {
                for (short j = 0; j < _rings - 1; j++)
                {
                    _domeIndexBuffer[index++] = (short)((i * _rings) + j);
                    _domeIndexBuffer[index++] = (short)(((i + 1) * _rings) + j);
                    _domeIndexBuffer[index++] = (short)(((i + 1) * _rings) + j + 1);
                    _domeIndexBuffer[index++] = (short)(((i + 1) * _rings) + j + 1);
                    _domeIndexBuffer[index++] = (short)((i * _rings) + j + 1);
                    _domeIndexBuffer[index++] = (short)((i * _rings) + j);
                }
            }

            short offset = (short)(_rings * _segments);

            for (short i = 0; i < _segments - 1; i++)
            {
                for (short j = 0; j < _rings - 1; j++)
                {
                    _domeIndexBuffer[index++] = (short)(offset + (i * _rings) + j);
                    _domeIndexBuffer[index++] = (short)(offset + ((i + 1) * _rings) + j + 1);
                    _domeIndexBuffer[index++] = (short)(offset + ((i + 1) * _rings) + j);
                    _domeIndexBuffer[index++] = (short)(offset + (i * _rings) + j + 1);
                    _domeIndexBuffer[index++] = (short)(offset + ((i + 1) * _rings) + j + 1);
                    _domeIndexBuffer[index++] = (short)(offset + (i * _rings) + j);
                }
            }
        }
    }
}
